
#include "scheduler.h"
#include "common.h"
#include "config.h"
#include "undertopomgr.h"
#include "eventdefs.h"
#include "simulator.h"
#include "peerloger.h"
#include "node.h"
#include "randomrangepro.h"


Scheduler* Scheduler::instance_ = NULL;

Scheduler::Scheduler()
	: Object()
	, disaplay_intv_(20.0)
{
	instance_ = this;
	NEW_OBJECT( DisplayTimer, disp_timer_,  (this) );
}

Scheduler::~Scheduler()
{
	RELEASE_OBJ( disp_timer_ );
}

bool Scheduler::create()
{
	NEW_OBJECT( Scheduler, instance_, () );
	return true;
}

void Scheduler::destroy()
{
	if ( instance_!=NULL ){
		RELEASE_OBJ( instance_ )
	}
}

bool Scheduler::scheduleAll()
{
	bool ret = scheduleNodes();

	disp_timer_->resched( disaplay_intv_ );

	return ret;
}

bool Scheduler::scheduleNodes()
{
	bool ret = false;
	switch( config.scheduler_.node_source ){
		case Config::SCH_SEQUENTIAL:
			ret = scheduleNodesSequential();
			break;
		case Config::SCH_RANDOM:
			ret = scheduleNodesRandom();
			break;
		case Config::SCH_TRACE:
			ret = scheduleNodesFromTrace( config.scheduler_.trace_file.c_str() );
			break;
		default:
			assert(false);
	}

	return ret;
}

bool Scheduler::scheduleNodesSequential()
{
	uint32_t n_size = UnderTopoMgr::instance().networkSize();
	sim_time_t int_arrv_t = config.scheduler_.interval;
	sim_time_t this_arrival_time = 0.0;
	uint32_t i = 0;

	for ( ; i<n_size; i++ ){
		//generate peer arrival and departure events
		NEW_VAR_OBJECT2( Event, NodeJoinEvent, event, (this, i) );
		event->setTime( this_arrival_time );
		Simulator::instance().scheduleEvent( event );
		RELEASE_OBJ( event );
		this_arrival_time += int_arrv_t;
	}

	uint32_t online_cnt = i;
	sim_time_t last_arrival_time = this_arrival_time;

	sim_time_t failure_time = config.scheduler_.failure_time;
	double     failure_portion = config.scheduler_.failure_portion;
	if ( failure_time>0 && failure_portion>0 ){
		RandomRangePro    rr(0,(uint32_t)online_cnt, (uint32_t)(n_size*failure_portion));
		//RandomRangePro    rr(0,(uint32_t)online_cnt, 3);
		while( rr.hasNext() ){ 
			i = rr.next();
			NEW_VAR_OBJECT2( Event, NodeDepartureEvent, event, (this,i) );
			event->setTime( failure_time );
			Simulator::instance().scheduleEvent( event );
			RELEASE_OBJ( event );
		}
	}

	PeerLoger::instance().logScheduler( 0, "%d nodes scheduled, last_arrival_time:%f", online_cnt, last_arrival_time );
	return true;
}

bool Scheduler::scheduleNodesRandom()
{
	uint32_t n_size = UnderTopoMgr::instance().networkSize();
	sim_time_t interval = config.scheduler_.interval;
	sim_time_t lifespan = config.scheduler_.lifespan;
	sim_time_t this_arrival_time = 0.0;

	uint32_t init_size = config.scheduler_.init_size;
	uint32_t i = 0;
	for ( ; i<init_size; i++ ){
		NEW_VAR_OBJECT2( Event, NodeJoinEvent, event, (this, i) );
		event->setTime( 0.0 );
		Simulator::instance().scheduleEvent( event );
		RELEASE_OBJ( event );
	}

	this_arrival_time = config.scheduler_.start_time;

	if ( config.scheduler_.start_time>=0 ){
		for ( ; i<n_size; i++ ){
			//generate peer arrival and departure events
			NEW_VAR_OBJECT2( Event, NodeJoinEvent, event_on, (this, i) );
			event_on->setTime( this_arrival_time );
			Simulator::instance().scheduleEvent( event_on );
			RELEASE_OBJ( event_on );

			sim_time_t duration = rng.expntl(lifespan);
			sim_time_t failure_time = this_arrival_time + duration;
			NEW_VAR_OBJECT2( Event, NodeDepartureEvent, event_off, (this,i) );
			event_off->setTime( failure_time );
			Simulator::instance().scheduleEvent( event_off );
			RELEASE_OBJ( event_off );

			PeerLoger::instance().logScheduler(0, "node:%u arrv_time:%f duration:%f off_time:%f", i, this_arrival_time, duration, failure_time);
			this_arrival_time += rng.expntl(interval);
		}
	}

	sim_time_t last_arrival_time = this_arrival_time;
	uint32_t online_cnt = i;
	PeerLoger::instance().logScheduler( 0, "%u nodes scheduled, last_arrival_time:%f", online_cnt, last_arrival_time );
	fprintf( stderr, "\n%u nodes scheduled, last_arrival_time:%f\n", online_cnt, last_arrival_time );

	return true;
}

bool Scheduler::scheduleNodesFromTrace(const char *filename)
{
	return false;
}

void Scheduler::onEvent( Event *event )
{
	if ( event->getType() ==  Event::NODE_JOIN ){
		NodeJoinEvent* join_event = static_cast<NodeJoinEvent*>(event);
		UnderTopoMgr::instance().node(join_event->nodeID())->onJoining();
	} else if ( event->getType() ==  Event::NODE_DEPARTURE ){
		NodeDepartureEvent* dept_event = static_cast<NodeDepartureEvent*>(event);
		UnderTopoMgr::instance().node(dept_event->nodeID())->onDeparture();
	} else if ( event->getType() == Event::COMMAND ){
		CmdEvent* cmd_event = static_cast<CmdEvent*>(event);
		executeCmd( cmd_event );
	} else{
			assert(false);
	}
}

void Scheduler::onTimer( Timer *timer )
{
	assert(false);
}

void Scheduler::executeCmd( CmdEvent *cmd_event )
{
	switch( cmd_event->cmd() ){
		default:
			assert(false);
	}
}

void Scheduler::displayProgress()
{
	sim_time_t now = Simulator::instance().clock();
	fprintf(stderr, "\rcur_time:%f            ", now);
	disp_timer_->resched( disaplay_intv_ );
}

